﻿/*********************************************************************************
**                                                                              **
**  Copyright (C) 2024-2025 LiLong                                              **
**  This file is part of OpenVisaApplication.                                   **
**                                                                              **
**  OpenVisaApplication is free software: you can redistribute it and/or modify **
**  it under the terms of the GNU General Public License as published by        **
**  the Free Software Foundation, either version 3 of the License, or           **
**  (at your option) any later version.                                         **
**                                                                              **
**  OpenVisaApplication is distributed in the hope that it will be useful,      **
**  but WITHOUT ANY WARRANTY; without even the implied warranty of              **
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               **
**  GNU General Public License for more details.                                **
**                                                                              **
**  You should have received a copy of the GNU General Public License           **
**  along with OpenVisaApplication. If not, see <https://www.gnu.org/licenses/>.**
**********************************************************************************/
#include "AttributeModel.h"

#include <OpenVisa/Attribute.h>

#include <ranges>

struct AttributeModel::Impl
{

    bool isSerialPort { false };
    std::shared_ptr<OpenVisa::Object> obj;
    std::vector<std::pair<Rows, QVariant>> attrs;
    const QVariant& attribute(Rows a) const
    {
        static QVariant empty;
        auto it = std::ranges::find_if(attrs,
                                       [&](const auto& attr)
                                       {
                                           return attr.first == a;
                                       });
        return it == attrs.end() ? empty : it->second;
    }
    void syncAttrs();
    void apply();
};

static QString toString(AttributeModel::Rows a)
{
    static constexpr auto list = { u8"终止符",     u8"启用终止符", u8"自动附近终止符", u8"超时时间",   u8"启用指令验证", u8"发送间隔时间",
                                   u8"串口波特率", u8"串口数据位", u8"串口停止位",     u8"串口流控制", u8"串口校验方式" };
    static constexpr auto v    = std::views::enumerate(list);
    return std::get<1>(v[a]);
}

void AttributeModel::Impl::syncAttrs()
{
    attrs.clear();
    const auto& a = obj->attribute();
    attrs.push_back({ TermChars, QByteArray::fromStdString(a.terminalChars()) });
    attrs.push_back({ TermCharsEnable, a.terminalCharsEnable() });
    attrs.push_back({ TermCharsAutoAppend, a.autoAppendTerminalChars() });
    attrs.push_back({ Timeout, a.timeout().count() });
    attrs.push_back({ CommandVerify, a.commandVerify() });
    attrs.push_back({ CommunicationInterval, a.communicationInterval().count() });
    if (isSerialPort)
    {
        attrs.push_back({ BaudRate, static_cast<int>(a.baudRate()) });
        attrs.push_back({ DataBits, static_cast<int>(a.dataBits()) });
        attrs.push_back({ StopBits, static_cast<int>(a.stopBits()) });
        attrs.push_back({ FlowControl, static_cast<int>(a.flowControl()) });
        attrs.push_back({ Parity, static_cast<int>(a.parity()) });
    }
}

void AttributeModel::Impl::apply()
{
    auto& a = obj->attribute();
    a.setTerminalChars(attribute(Rows::TermChars).toByteArray().toStdString());
    a.setTerminalCharsEnable(attribute(Rows::TermCharsEnable).toBool());
    a.setAutoAppendTerminalChars(attribute(Rows::TermCharsAutoAppend).toBool());
    a.setTimeout(std::chrono::milliseconds(attribute(Rows::Timeout).toLongLong()));
    a.setCommandVerify(attribute(Rows::CommandVerify).toBool());
    a.setCommunicationInterval(std::chrono::milliseconds(attribute(Rows::CommunicationInterval).toLongLong()));
    if (isSerialPort)
    {
        a.setBaudRate(attribute(Rows::BaudRate).toUInt());
        a.setDataBits(static_cast<OpenVisa::DataBits>(attribute(Rows::DataBits).toInt()));
        a.setStopBits(static_cast<OpenVisa::StopBits>(attribute(Rows::StopBits).toInt()));
        a.setFlowControl(static_cast<OpenVisa::FlowControl>(attribute(Rows::FlowControl).toInt()));
        a.setParity(static_cast<OpenVisa::Parity>(attribute(Rows::Parity).toInt()));
    }
}

AttributeModel::AttributeModel(QObject* parent) : QAbstractTableModel(parent), m_impl(std::make_unique<Impl>())
{
}

AttributeModel::~AttributeModel()
{
}

int AttributeModel::rowCount(const QModelIndex& parent /*= QModelIndex()*/) const
{
    return static_cast<int>(m_impl->attrs.size());
}

int AttributeModel::columnCount(const QModelIndex& parent /*= QModelIndex()*/) const
{
    return 2;
}

QVariant AttributeModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const
{
    if (role == Qt::DisplayRole || role == Qt::EditRole)
    {
        const auto& r = m_impl->attrs.at(index.row());
        return index.column() == 0 ? toString(r.first) : r.second;
    }
    return {};
}

QVariant AttributeModel::headerData(int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/) const
{
    if (role == Qt::DisplayRole && orientation == Qt::Horizontal)
    {
        return section == 0 ? QString(u8"属性") : u8"值";
    }
    return {};
}

bool AttributeModel::setData(const QModelIndex& index, const QVariant& value, int role /*= Qt::EditRole*/)
{
    if (role == Qt::EditRole)
    {
        if (index.column() == 1)
        {
            m_impl->attrs.at(index.row()).second = value;
            emit dataChanged(index, index, { role });
            return true;
        }
    }
    return false;
}

Qt::ItemFlags AttributeModel::flags(const QModelIndex& index) const
{
    return index.column() == 0 ? Qt::ItemIsEnabled | Qt::ItemIsSelectable : Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}

void AttributeModel::setObject(std::shared_ptr<OpenVisa::Object> obj, bool isSerialPort)
{
    beginResetModel();
    m_impl->obj          = obj;
    m_impl->isSerialPort = isSerialPort;
    m_impl->syncAttrs();
    endResetModel();
}

void AttributeModel::apply()
{
    m_impl->apply();
}
